<!DOCTYPE html><html lang="zh-Hans"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"><meta name="description" content="react简单学习记录"><meta name="keywords" content="react redux hook"><meta name="author" content="晟松"><meta name="copyright" content="晟松"><title>react简单学习记录 | 晟松</title><link rel="shortcut icon" href="/logo.png"><link rel="stylesheet" href="/css/index.css?version=1.9.0"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@latest/css/font-awesome.min.css?version=1.9.0"><meta name="format-detection" content="telephone=no"><meta http-equiv="x-dns-prefetch-control" content="on"><link rel="dns-prefetch" href="https://cdn.jsdelivr.net"><link rel="dns-prefetch" href="https://hm.baidu.com"><script>var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?c34d81d53341febe06a155941e2df16d";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();</script><meta http-equiv="Cache-Control" content="no-transform"><meta http-equiv="Cache-Control" content="no-siteapp"><script>var GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: {"path":"search.xml","languages":{"hits_empty":"找不到您查询的内容:${query}"}},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  hexoVersion: '5.4.0'
} </script><meta name="generator" content="Hexo 5.4.0"></head><body><i class="fa fa-arrow-right" id="toggle-sidebar" aria-hidden="true"></i><div id="sidebar" data-display="true"><div class="toggle-sidebar-info text-center"><span data-toggle="切换文章详情">切换站点概览</span><hr></div><div class="sidebar-toc"><div class="sidebar-toc__title">目录</div><div class="sidebar-toc__progress"><span class="progress-notice">你已经读了</span><span class="progress-num">0</span><span class="progress-percentage">%</span><div class="sidebar-toc__progress-bar"></div></div><div class="sidebar-toc__content"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#react%E7%AE%80%E5%8D%95%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95"><span class="toc-number">1.</span> <span class="toc-text">react简单学习记录</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%9F%BA%E6%9C%AC%E8%AF%AD%E6%B3%95"><span class="toc-number">1.1.</span> <span class="toc-text">基本语法</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%BB%84%E4%BB%B6"><span class="toc-number">1.1.1.</span> <span class="toc-text">组件</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F"><span class="toc-number">1.1.2.</span> <span class="toc-text">生命周期</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86"><span class="toc-number">1.1.3.</span> <span class="toc-text">事件处理</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%9D%A1%E4%BB%B6%E6%B8%B2%E6%9F%93"><span class="toc-number">1.1.4.</span> <span class="toc-text">条件渲染</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%97%E8%A1%A8-amp-key"><span class="toc-number">1.1.5.</span> <span class="toc-text">列表&amp;key</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%A1%A8%E5%8D%95"><span class="toc-number">1.1.6.</span> <span class="toc-text">表单</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%8A%B6%E6%80%81%E6%8F%90%E5%8D%87"><span class="toc-number">1.1.7.</span> <span class="toc-text">状态提升</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%BB%84%E4%BB%B6%E6%B8%B2%E6%9F%93"><span class="toc-number">1.1.8.</span> <span class="toc-text">组件渲染</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%AB%98%E7%BA%A7%E6%8C%87%E5%BC%95"><span class="toc-number">1.2.</span> <span class="toc-text">高级指引</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#hooks"><span class="toc-number">1.3.</span> <span class="toc-text">hooks</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#state"><span class="toc-number">1.3.1.</span> <span class="toc-text">state</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#useEffect"><span class="toc-number">1.3.2.</span> <span class="toc-text">useEffect</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#useCallback"><span class="toc-number">1.3.3.</span> <span class="toc-text">useCallback</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#useMemo"><span class="toc-number">1.3.4.</span> <span class="toc-text">useMemo</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#react-router"><span class="toc-number">1.4.</span> <span class="toc-text">react-router</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#redux"><span class="toc-number">1.5.</span> <span class="toc-text">redux</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%B8%B8%E8%A7%81%E6%A6%82%E5%BF%B5"><span class="toc-number">1.5.1.</span> <span class="toc-text">常见概念</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#state-1"><span class="toc-number">1.5.1.1.</span> <span class="toc-text">state</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#action"><span class="toc-number">1.5.1.2.</span> <span class="toc-text">action</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#reducer"><span class="toc-number">1.5.1.3.</span> <span class="toc-text">reducer</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8E%9F%E7%94%9F%E6%90%AD%E9%85%8Dreact%E7%9A%84%E4%BD%BF%E7%94%A8"><span class="toc-number">1.5.2.</span> <span class="toc-text">原生搭配react的使用</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%BC%A0%E5%85%A5store"><span class="toc-number">1.5.2.1.</span> <span class="toc-text">传入store</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Redux-%E6%95%B0%E6%8D%AE%E6%B5%81"><span class="toc-number">1.5.3.</span> <span class="toc-text">Redux 数据流</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8slice-reducer%E8%AF%AD%E6%B3%95%E7%B3%96"><span class="toc-number">1.5.4.</span> <span class="toc-text">使用slice reducer语法糖</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#proxy-immer%E4%BD%BF%E7%94%A8"><span class="toc-number">1.5.4.1.</span> <span class="toc-text">proxy immer使用</span></a><ol class="toc-child"><li class="toc-item toc-level-6"><a class="toc-link" href="#%E8%AD%A6%E5%91%8A"><span class="toc-number">1.5.4.1.1.</span> <span class="toc-text">警告</span></a></li></ol></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BC%82%E6%AD%A5%E9%80%BB%E8%BE%91-thunk"><span class="toc-number">1.5.5.</span> <span class="toc-text">异步逻辑    thunk</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#useSelector%E5%92%8CuseDispatch"><span class="toc-number">1.5.6.</span> <span class="toc-text">useSelector和useDispatch</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#prepare%E5%87%BD%E6%95%B0"><span class="toc-number">1.5.7.</span> <span class="toc-text">prepare函数</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%85%B6%E4%BB%96"><span class="toc-number">1.6.</span> <span class="toc-text">其他</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%89%B9%E5%88%AB%E6%B3%A8%E6%84%8F"><span class="toc-number">1.6.1.</span> <span class="toc-text">特别注意</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%87%E6%8D%A2%E4%B8%BB%E9%A2%98"><span class="toc-number">1.6.2.</span> <span class="toc-text">切换主题</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%89%B9%E6%AE%8A%E5%B0%8F%E6%8A%80%E5%B7%A7"><span class="toc-number">1.6.3.</span> <span class="toc-text">特殊小技巧</span></a></li></ol></li></ol></li></ol></div></div><div class="author-info hide"><div class="author-info__avatar text-center"><img src="/img/avatar.png"></div><div class="author-info__name text-center">晟松</div><div class="author-info__description text-center">晟松的博客</div><div class="follow-button"><a target="_blank" rel="noopener" href="https://gitee.com/cheng_song">Gitee</a></div><hr><div class="author-info-articles"><a class="author-info-articles__archives article-meta" href="/archives"><span class="pull-left">文章</span><span class="pull-right">52</span></a><a class="author-info-articles__tags article-meta" href="/tags"><span class="pull-left">标签</span><span class="pull-right">45</span></a></div></div></div><div id="content-outer"><div id="top-container" style="background-image: url(/img/blog-bg.jpg)"><div id="page-header"><span class="pull-left"> <a id="site-name" href="/">晟松</a></span><i class="fa fa-bars toggle-menu pull-right" aria-hidden="true"></i><span class="pull-right menus">   <a class="site-page" href="/">Home</a><a class="site-page" href="/archives">Archives</a><a class="site-page" href="/tags">Tags</a><a class="site-page" href="/categories">Categories</a></span><span class="pull-right"><a class="site-page social-icon search"><i class="fa fa-search"></i><span> 搜索</span></a></span></div><div id="post-info"><div id="post-title">react简单学习记录</div><div id="post-meta"><time class="post-meta__date"><i class="fa fa-calendar" aria-hidden="true"></i> 2021-11-19</time><div class="post-meta-wordcount"><span>字数总计: </span><span class="word-count">6.1k</span><span class="post-meta__separator">|</span><span>阅读时长: 25 分钟</span></div></div></div></div><div class="layout" id="content-inner"><article id="post"><div class="article-container" id="post-content"><h2 id="react简单学习记录"><a href="#react简单学习记录" class="headerlink" title="react简单学习记录"></a>react简单学习记录</h2><p>隔了挺久再次开始写博客了，距上一篇大概差不多3个月了吧。这里面大概半个月准备考试，后面两个月在实习，然后再这个实习里面学一点新东西，就这样。虽然实际的写这个react还是在这一周左右，简单的写一下这一周写了几天代码后的感觉和知识点吧</p>
<p><a target="_blank" rel="noopener" href="https://react.docschina.org/docs/hooks-effect.html">react官方中文文档</a></p>
<p><a target="_blank" rel="noopener" href="https://gitee.com/Darkflamer09/react-study/tree/master">一个入门的示例代码</a></p>
<p>因为这个项目写的代码是公司的，所以就不放了。而且代码是我接手另一个实习生的，代码写的极其混乱，就这样吧。</p>
<p>这边是用ts写的，可能有点差别吧</p>
<p>因为之前是学了挺久的vue的，所以可能会将一些vue里面的东西和react进行对比着看了.</p>
<p>因为主要是写给自己看的，所以一些比较相同的使用的东西可能就不记了，比如diff，虚拟dom啥的</p>
<h3 id="基本语法"><a href="#基本语法" class="headerlink" title="基本语法"></a>基本语法</h3><ul>
<li><p>首先就是jsx或tsx的语法问题了，这个东西吧写着也不难，跟vue里的template差不多，只不过，在vue里面插入一些值是用 <strong>&lt;!–swig￼32–&gt;</strong> 这里是直接使用 <strong>{ }</strong> 同时这个里面是可以使用js表达式的，就是说可以用一些三目运算符，或者函数（使用其返回结果）</p>
</li>
<li><p>另一个差别点就是class了，react里面使用 <strong>className</strong> 来指定class，还有一个就是标签名了。reatc里面使用大写开头的标签名，而vue里面使用短横线进行标签名指定</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line">react</span><br><span class="line"><span class="tag">&lt;<span class="name">Col</span> <span class="attr">span</span>=<span class="string">&#123;2&#125;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">Checkbox</span></span></span><br><span class="line"><span class="tag">      <span class="attr">indeterminate</span>=<span class="string">&#123;indeterminate&#125;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">onChange</span>=<span class="string">&#123;onCheckAllChange&#125;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">checked</span>=<span class="string">&#123;checkAll&#125;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">className</span>=<span class="string">&#123;</span>&quot;<span class="attr">flex-align-item-start</span>&quot;&#125;</span></span><br><span class="line"><span class="tag">    &gt;</span><span class="tag">&lt;/<span class="name">Checkbox</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">Col</span>&gt;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">vue</span><br><span class="line"><span class="tag">&lt;<span class="name">a-input</span> <span class="attr">v-model:value</span>=<span class="string">&quot;username&quot;</span> <span class="attr">placeholder</span>=<span class="string">&quot;Username&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">template</span> #<span class="attr">prefix</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">UserOutlined</span> <span class="attr">style</span>=<span class="string">&quot;color:rgba(0,0,0,.25)&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">a-input</span>&gt;</span></span><br></pre></td></tr></table></figure></li>
</ul>
<h4 id="组件"><a href="#组件" class="headerlink" title="组件"></a>组件</h4><p>组件的概念都差不了太多，在vue里面需要使用props进行指定哪一些参数需要通过props传入，而在react里面，因为使用的是ts，同时是使用的函数式组件，所以可以指定函数传入的参数的类型来进行props指定</p>
<p>同时在react的函数组件里面是使用函数参数名进行传来的props使用 props.title、props.onchange……</p>
<h4 id="生命周期"><a href="#生命周期" class="headerlink" title="生命周期"></a>生命周期</h4><p>这个东西似乎在明面上只在class组件里面存在，如componentDidMount、componentWillUnmount等</p>
<p>而在函数式组件里面就是用hooks进行了替代，就先不记了</p>
<h4 id="事件处理"><a href="#事件处理" class="headerlink" title="事件处理"></a>事件处理</h4><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">&#123;activateLasers&#125;</span>&gt;</span></span><br><span class="line">  Activate Lasers</span><br><span class="line"><span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onClick</span>=<span class="string">&#123;(e)</span>=&gt;</span>&#123;activateLasers(e,other)&#125;&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">button</span>&gt;</span>&#125;&gt;</span><br><span class="line">  Activate Lasers</span><br><span class="line"><span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>react的事件处理差不多就这样子，直接传入一个函数，或者使用回调函数自己进行控制，比如需要一些在这个特定作用域里面的参数（比如map循环生成tsx代码里面的index）</p>
<h4 id="条件渲染"><a href="#条件渲染" class="headerlink" title="条件渲染"></a>条件渲染</h4><p>这个东西就没啥说的，因为在 {} 里面是使用表达式，而不是代码（不如if(){ }else{}）这种判断代码，所以一般是使用三目运算符，或 &amp;&amp; 来进行简单的表达式运算得到一个返回值进行渲染</p>
<p>或者直接写一个函数，在函数里面进行判断，然后返回对应的tsx代码</p>
<h4 id="列表-amp-key"><a href="#列表-amp-key" class="headerlink" title="列表&amp;key"></a>列表&amp;key</h4><p>这个东西跟 v-for 差不多，只不多v-for是在template里面的标签里面进行使用，而这里是在一些函数里面进行使用，然后返回</p>
<p>常用的循环渲染方式是 <strong>map</strong> 函数，因为会返回一个变化后的数组，然后进行渲染。注意需要指定key，跟vue里面差不多</p>
<p>如果没有指定key，默认就用index当做key</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> listItems = numbers.map(<span class="function">(<span class="params">number</span>) =&gt;</span></span><br><span class="line">    <span class="comment">// 正确！key 应该在数组的上下文中被指定</span></span><br><span class="line">    <span class="xml"><span class="tag">&lt;<span class="name">ListItem</span> <span class="attr">key</span>=<span class="string">&#123;number.toString()&#125;</span>  <span class="attr">value</span>=<span class="string">&#123;number&#125;</span> /&gt;</span></span></span><br><span class="line">  );</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    <span class="xml"><span class="tag">&lt;<span class="name">ul</span>&gt;</span></span></span><br><span class="line"><span class="xml">      &#123;listItems&#125;</span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span></span><br><span class="line">  );</span><br></pre></td></tr></table></figure>

<p><strong>特别注意</strong>：如果在map循环里面的return使用了if判断进行，同时加了key，那么在所有的条件返回里面都需要加上key，如果一个加，一个不加，很可能产生各种奇奇怪怪的渲染问题</p>
<h4 id="表单"><a href="#表单" class="headerlink" title="表单"></a>表单</h4><p>react并没有vue里面的 v-model 双向绑定的东西，我们需要在组件里面监听html组件的onchange事件，并同步数据到代码里面的值。同时在代码里面的值修改后，还要同步到html组件里面，就很烦。一般使用antd的form组件的时候，其会自动的给你相连接，不需要手动的写代码进行同步</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">this</span>.state = &#123;<span class="attr">value</span>: <span class="string">&#x27;&#x27;</span>&#125;;</span><br><span class="line"><span class="function"><span class="title">handleChange</span>(<span class="params">event</span>)</span> &#123;</span><br><span class="line">	<span class="built_in">this</span>.setState(&#123;<span class="attr">value</span>: event.target.value&#125;);</span><br><span class="line">&#125;</span><br><span class="line">&lt;input type=<span class="string">&quot;text&quot;</span> value=&#123;<span class="built_in">this</span>.state.value&#125; onChange=&#123;<span class="built_in">this</span>.handleChange&#125; /&gt;</span><br></pre></td></tr></table></figure>

<h4 id="状态提升"><a href="#状态提升" class="headerlink" title="状态提升"></a>状态提升</h4><p>这东西就是把子组件的内部数据变为从父组件传来的props的数据。同时不断地进行同步，父组件在接收多个子组件的数据，然后干一些事情，就这样。</p>
<h4 id="组件渲染"><a href="#组件渲染" class="headerlink" title="组件渲染"></a>组件渲染</h4><p>父组件在使用子函数组件的时候，我们会通过props传递一些数据进去。当这些数据发生在父组件变化的时候，子组件就会重新渲染</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">&lt;DetailedList</span><br><span class="line">  deleteList=&#123;deleteList&#125;</span><br><span class="line">  list=&#123;detailedLists&#125;</span><br><span class="line">  productType=&#123;productType&#125;</span><br><span class="line">  provider=&#123;provider&#125;</span><br><span class="line">&gt;&lt;/DetailedList&gt;</span><br></pre></td></tr></table></figure>

<p>当里面的任意值发生变化，DetailedList这个子组件就会重新渲染。同时在detailedLists修改后，deleteList在父组件里面也会同步更新</p>
<p>所以如果你在DetailedList里面使用了console，你就可能会看见多次console在detailedLists一次更新之后</p>
<h3 id="高级指引"><a href="#高级指引" class="headerlink" title="高级指引"></a>高级指引</h3><p>这个东西吧，我还没用到这些比较高级的用法，就先放着吧。</p>
<h3 id="hooks"><a href="#hooks" class="headerlink" title="hooks"></a>hooks</h3><p>重点来了</p>
<p>这个东西感觉跟vue的组合式api感觉很像，但也没细看，也不知道这两者到底有多大区别</p>
<p>只能在函数式组件中使用</p>
<h4 id="state"><a href="#state" class="headerlink" title="state"></a>state</h4><p>注意里面的setState方法是异步更新的，所以不能再立即使用了set后，就去获取最新的值。如果需要监听最新的值的话，另外使用一个useEffect，第二个参数为这个state，这样在setState异步更新后，effect就能立刻知道所依赖的state更新了，然后执行effect</p>
<h4 id="useEffect"><a href="#useEffect" class="headerlink" title="useEffect"></a>useEffect</h4><p><a target="_blank" rel="noopener" href="https://blog.csdn.net/glorydx/article/details/114107703">简单使用</a></p>
<p><strong>如果useEffect第一个函数参数直接或者间接用上某个变量，就请把这个变量放在useEffect的第二个参数里</strong></p>
<p><strong>使用useEffect，不要调用函数层次太多，代码应该一眼看清楚哪些函数会被useEffect调用</strong>。</p>
<p><strong>useEffect的第一个函数参数在初始化的时候也会执行一次</strong></p>
<p>每一次的useEffect执行都会<strong>记忆当时的作用域</strong>，如果这个effect不<strong>重新执行</strong>的话，那么这个作用域里面的值就一直不会更新</p>
<p>当数组里面的值被修改了之后，这个函数就会再次被执行。注意这个react似乎是使用object.is来比较两次渲染更新后数组里面的每一个值是否发生变化，所以可能就无法监听到数组的值的变化，或者是数组的新增（跟vue2差不多）。因为一般修改数组只是修改了堆里面的数据，而在栈里面的地址没有变化。所以一般来说在effect里面就不要直接修改依赖项的地址的值了</p>
<p><strong>无限循环</strong> 在useEffect执行函数里面改变了useEffect监测的变量</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">const [a, setA] = useState(1);</span><br><span class="line">useEffect(() =&gt; &#123;</span><br><span class="line">/** 执行逻辑 */</span><br><span class="line">setA(a + 1)</span><br><span class="line">&#125;,[a])</span><br></pre></td></tr></table></figure>

<p>解决的方法 <strong>不要在useEffect第一参数执行函数中去改变第二参数依赖元素的地址的值</strong>。当依赖元素的地址的值发生改变，又会执行一次执行函数，这不是无限循环么。</p>
<h4 id="useCallback"><a href="#useCallback" class="headerlink" title="useCallback"></a>useCallback</h4><p><a target="_blank" rel="noopener" href="https://blog.csdn.net/weixin_45389051/article/details/107677723">简单使用</a></p>
<p>这个hook也具有闭包的特性，第二个参数也是一个数组。如果你需要这个值，那么你就加入到这个数组里面。这里似乎就没有需要注意不要改变地址的值的限制了，因为这个只是将函数的一个作用域进行了一下刷新，其他的似乎也没干啥了</p>
<p><strong>在使用值和函数的情况，有函数传给子组件调用的时候要使用useCallback。</strong></p>
<p>useCallback(fn, deps) 相当于 useMemo(() =&gt; fn, deps)。</p>
<p>简单来说： useCallback 就是处理父组件重复无用的调用子组件的。 useMemo 就是处理重复无用的调用属性的。</p>
<h4 id="useMemo"><a href="#useMemo" class="headerlink" title="useMemo"></a>useMemo</h4><p><a target="_blank" rel="noopener" href="https://blog.csdn.net/MRlaochen/article/details/119025711">简单使用</a></p>
<p>都是进行一个缓存，只不过useCallback是缓存一个函数，当依赖变了，就刷新一下这个函数。而useMemo是缓存一个变量，当依赖发生改变的时候，就更新这个变量。有点类似vue里面的computed</p>
<h3 id="react-router"><a href="#react-router" class="headerlink" title="react-router"></a>react-router</h3><p>感觉这个跟vue-router差不多，很多用法都很相似。基本上都是路由配置，然后传入Router标签进行渲染</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> routeConfig = [</span><br><span class="line">  &#123; <span class="attr">path</span>: <span class="string">&#x27;/&#x27;</span>,</span><br><span class="line">    <span class="attr">component</span>: App,</span><br><span class="line">    <span class="attr">indexRoute</span>: &#123; <span class="attr">component</span>: Dashboard &#125;,</span><br><span class="line">    <span class="attr">childRoutes</span>: [</span><br><span class="line">      &#123; <span class="attr">path</span>: <span class="string">&#x27;about&#x27;</span>, <span class="attr">component</span>: About &#125;,</span><br><span class="line">      &#123; <span class="attr">path</span>: <span class="string">&#x27;inbox&#x27;</span>,</span><br><span class="line">        <span class="attr">component</span>: Inbox,</span><br><span class="line">        <span class="attr">childRoutes</span>: [</span><br><span class="line">          &#123; <span class="attr">path</span>: <span class="string">&#x27;/messages/:id&#x27;</span>, <span class="attr">component</span>: Message &#125;,</span><br><span class="line">          &#123; <span class="attr">path</span>: <span class="string">&#x27;messages/:id&#x27;</span>,</span><br><span class="line">            <span class="attr">onEnter</span>: <span class="function"><span class="keyword">function</span> (<span class="params">nextState, replaceState</span>) </span>&#123;</span><br><span class="line">              replaceState(<span class="literal">null</span>, <span class="string">&#x27;/messages/&#x27;</span> + nextState.params.id)</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;</span><br><span class="line">        ]</span><br><span class="line">      &#125;</span><br><span class="line">    ]</span><br><span class="line">  &#125;</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">React.render(<span class="xml"><span class="tag">&lt;<span class="name">Router</span> <span class="attr">routes</span>=<span class="string">&#123;routeConfig&#125;</span> /&gt;</span></span>, <span class="built_in">document</span>.body)</span><br></pre></td></tr></table></figure>

<p>其他的类似路由守卫，动态路由的东西也有，需要时再看吧</p>
<h3 id="redux"><a href="#redux" class="headerlink" title="redux"></a>redux</h3><p>怎么说呢，redux这个东西感觉是一种更抽象的东西，是一种满足了一定规则的通用做法。不限于特定的语言还是啥，只要符合其设定的规则，其他的都可以用</p>
<h4 id="常见概念"><a href="#常见概念" class="headerlink" title="常见概念"></a>常见概念</h4><h5 id="state-1"><a href="#state-1" class="headerlink" title="state"></a>state</h5><p>跟vuex差不多，都是一个单一数据源进行数据存储。<strong>只读</strong></p>
<h5 id="action"><a href="#action" class="headerlink" title="action"></a>action</h5><p>这里就跟vuex有区别了，vuex里面的action是异步的修改state里面的数据，而在这里就是一个普通 JavaScript 对象</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">&#123; <span class="attr">type</span>: <span class="string">&#x27;ADD_TODO&#x27;</span>, <span class="attr">text</span>: <span class="string">&#x27;Go to swimming pool&#x27;</span> &#125;</span><br><span class="line">&#123; <span class="attr">type</span>: <span class="string">&#x27;TOGGLE_TODO&#x27;</span>, <span class="attr">index</span>: <span class="number">1</span> &#125;</span><br><span class="line">&#123; <span class="attr">type</span>: <span class="string">&#x27;SET_VISIBILITY_FILTER&#x27;</span>, <span class="attr">filter</span>: <span class="string">&#x27;SHOW_ALL&#x27;</span> &#125;</span><br></pre></td></tr></table></figure>

<p>或者使用action创建函数</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">addTodo</span>(<span class="params">text</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    <span class="attr">type</span>: ADD_TODO,</span><br><span class="line">    text</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 调用</span></span><br><span class="line">dispatch(addTodo(text))</span><br></pre></td></tr></table></figure>



<h5 id="reducer"><a href="#reducer" class="headerlink" title="reducer"></a>reducer</h5><p>这个就是将state和action连接起来的东西。Reducer 只是一些纯函数，它接收先前的 state 和 action，并返回新的 state</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">todos</span>(<span class="params">state = [], action</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">switch</span> (action.type) &#123;</span><br><span class="line">  <span class="keyword">case</span> <span class="string">&#x27;ADD_TODO&#x27;</span>:</span><br><span class="line">    <span class="keyword">return</span> state.concat([&#123; <span class="attr">text</span>: action.text, <span class="attr">completed</span>: <span class="literal">false</span> &#125;]);</span><br><span class="line">  <span class="keyword">case</span> <span class="string">&#x27;TOGGLE_TODO&#x27;</span>:</span><br><span class="line">    <span class="keyword">return</span> state.map(<span class="function">(<span class="params">todo, index</span>) =&gt;</span></span><br><span class="line">      action.index === index ?</span><br><span class="line">        &#123; <span class="attr">text</span>: todo.text, <span class="attr">completed</span>: !todo.completed &#125; :</span><br><span class="line">        todo</span><br><span class="line">   )</span><br><span class="line">  <span class="attr">default</span>:</span><br><span class="line">    <span class="keyword">return</span> state;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>最简单的概念就是如此了</p>
<h4 id="原生搭配react的使用"><a href="#原生搭配react的使用" class="headerlink" title="原生搭配react的使用"></a><a target="_blank" rel="noopener" href="https://www.redux.org.cn/docs/basics/UsageWithReact.html">原生搭配react的使用</a></h4><p><a target="_blank" rel="noopener" href="https://www.redux.org.cn/docs/basics/ExampleTodoList.html">完整代码</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/yoonerloop/article/details/112058929">connect方法解析</a></p>
<p>似乎是因为组件不能直接引入store，所以使用了一个中间层来进行对store里的数据的获取与更新</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">import &#123; connect &#125; from &#x27;react-redux&#x27;</span><br><span class="line">import &#123; setVisibilityFilter &#125; from &#x27;../actions&#x27;</span><br><span class="line">import Link from &#x27;../components/Link&#x27;</span><br><span class="line"></span><br><span class="line">const mapStateToProps = (state, ownProps) =&gt; &#123;</span><br><span class="line">  return &#123;</span><br><span class="line">    active: ownProps.filter === state.visibilityFilter</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">const mapDispatchToProps = (dispatch, ownProps) =&gt; &#123;</span><br><span class="line">  return &#123;</span><br><span class="line">    onClick: () =&gt; &#123;</span><br><span class="line">      dispatch(setVisibilityFilter(ownProps.filter))</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 注意connect这个api和上面两个方法</span><br><span class="line">const FilterLink = connect(</span><br><span class="line">  mapStateToProps,</span><br><span class="line">  mapDispatchToProps</span><br><span class="line">)(Link)</span><br><span class="line"></span><br><span class="line">export default FilterLink</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">import React from &#x27;react&#x27;</span><br><span class="line">import PropTypes from &#x27;prop-types&#x27;</span><br><span class="line"></span><br><span class="line">const Link = (&#123; active, children, onClick &#125;) =&gt; &#123;</span><br><span class="line">  if (active) &#123;</span><br><span class="line">    return &lt;span&gt;&#123;children&#125;&lt;/span&gt;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  return (</span><br><span class="line">    &lt;a</span><br><span class="line">      href=&quot;&quot;</span><br><span class="line">      onClick=&#123;e =&gt; &#123;</span><br><span class="line">        e.preventDefault()</span><br><span class="line">        onClick()</span><br><span class="line">      &#125;&#125;</span><br><span class="line">    &gt;</span><br><span class="line">      &#123;children&#125;</span><br><span class="line">    &lt;/a&gt;</span><br><span class="line">  )</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Link.propTypes = &#123;</span><br><span class="line">  active: PropTypes.bool.isRequired,</span><br><span class="line">  children: PropTypes.node.isRequired,</span><br><span class="line">  onClick: PropTypes.func.isRequired</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">export default Link</span><br></pre></td></tr></table></figure>



<h5 id="传入store"><a href="#传入store" class="headerlink" title="传入store"></a>传入store</h5><p>使用provider</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">import React from &#x27;react&#x27;</span><br><span class="line">import &#123; render &#125; from &#x27;react-dom&#x27;</span><br><span class="line">import &#123; Provider &#125; from &#x27;react-redux&#x27;</span><br><span class="line">import &#123; createStore &#125; from &#x27;redux&#x27;</span><br><span class="line">import todoApp from &#x27;./reducers&#x27;</span><br><span class="line">import App from &#x27;./components/App&#x27;</span><br><span class="line"></span><br><span class="line">let store = createStore(todoApp)</span><br><span class="line"></span><br><span class="line">render(</span><br><span class="line">  &lt;Provider store=&#123;store&#125;&gt;</span><br><span class="line">    &lt;App /&gt;</span><br><span class="line">  &lt;/Provider&gt;,</span><br><span class="line">  document.getElementById(&#x27;root&#x27;)</span><br><span class="line">)</span><br></pre></td></tr></table></figure>



<p>后面还有一些高级概念，比如异步action，中间件等</p>
<h4 id="Redux-数据流"><a href="#Redux-数据流" class="headerlink" title="Redux 数据流"></a><a target="_blank" rel="noopener" href="http://cn.redux.js.org/tutorials/essentials/part-1-overview-concepts#redux-%E6%95%B0%E6%8D%AE%E6%B5%81">Redux 数据流</a></h4><p>早些时候，我们谈到了“单向数据流”，它描述了更新应用程序的以下步骤序列：</p>
<ul>
<li>State 描述了应用程序在特定时间点的状况</li>
<li>基于 state 来渲染 UI</li>
<li>当发生某些事情时（例如用户单击按钮），state 会根据发生的事情进行更新</li>
<li>基于新的 state 重新渲染 UI</li>
</ul>
<p>具体来说，对于 Redux，我们可以将这些步骤分解为更详细的内容：</p>
<ul>
<li>初始启动：<ul>
<li>使用最顶层的 root reducer 函数创建 Redux store</li>
<li>store 调用一次 root reducer，并将<strong>返回值保存为它的初始 <code>state</code></strong></li>
<li>当 UI 首次渲染时，UI 组件访问 Redux store 的当前 state，并使用该数据来决定要呈现的内容。同时监听 store 的更新，以便他们可以知道 state 是否已更改。</li>
</ul>
</li>
<li>更新环节：<ul>
<li>应用程序中发生了某些事情，例如用户单击按钮</li>
<li>dispatch 一个 action 到 Redux store，例如 <code>dispatch(&#123;type: &#39;counter/increment&#39;&#125;)</code></li>
<li>store 用之前的 <code>state</code> 和当前的 <code>action</code> 再次运行 reducer 函数，并将返回值保存为新的 <code>state</code></li>
<li>store 通知所有订阅过的 UI，通知它们 store 发生更新</li>
<li>每个订阅过 store 数据的 UI 组件都会检查它们需要的 state 部分是否被更新。</li>
<li>发现数据被更新的每个组件都强制使用新数据重新渲染，紧接着更新网页</li>
</ul>
</li>
</ul>
<h4 id="使用slice-reducer语法糖"><a href="#使用slice-reducer语法糖" class="headerlink" title="使用slice reducer语法糖"></a><a target="_blank" rel="noopener" href="https://codesandbox.io/s/github/reduxjs/redux-essentials-counter-example/tree/master/?from-embed=&file=/src/index.js">使用slice reducer语法糖</a></h4><p><a target="_blank" rel="noopener" href="http://cn.redux.js.org/tutorials/essentials/part-2-app-structure/#%E7%94%A8-thunk-%E7%BC%96%E5%86%99%E5%BC%82%E6%AD%A5%E9%80%BB%E8%BE%91">使用文档</a></p>
<p>这种方式就很像vuex的样子了，然后理解感觉也更好了。</p>
<p>使用了Redux Toolkit</p>
<p>有初始值，有对应的分模块，有对应的reducer，真好</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">import React from &#x27;react&#x27;;</span><br><span class="line">import ReactDOM from &#x27;react-dom&#x27;;</span><br><span class="line">import &#x27;./index.css&#x27;;</span><br><span class="line">import App from &#x27;./App&#x27;;</span><br><span class="line">import store from &#x27;./app/store&#x27;;</span><br><span class="line">import &#123; Provider &#125; from &#x27;react-redux&#x27;;</span><br><span class="line"></span><br><span class="line">ReactDOM.render(</span><br><span class="line">  &lt;Provider store=&#123;store&#125;&gt;</span><br><span class="line">    &lt;App /&gt;</span><br><span class="line">  &lt;/Provider&gt;,</span><br><span class="line">  document.getElementById(&#x27;root&#x27;)</span><br><span class="line">);</span><br></pre></td></tr></table></figure>



<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">// app/store</span><br><span class="line">import &#123; configureStore &#125; from &#x27;@reduxjs/toolkit&#x27;</span><br><span class="line">import counterReducer from &#x27;../features/counter/counterSlice&#x27;</span><br><span class="line"></span><br><span class="line">export default configureStore(&#123;</span><br><span class="line">  reducer: &#123;</span><br><span class="line">    counter: counterReducer</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">// features/counter/counterSlice.js</span><br><span class="line"></span><br><span class="line">import &#123; createSlice &#125; from &#x27;@reduxjs/toolkit&#x27;</span><br><span class="line"></span><br><span class="line">export const counterSlice = createSlice(&#123;</span><br><span class="line">  name: &#x27;counter&#x27;,</span><br><span class="line">  initialState: &#123;</span><br><span class="line">    value: 0</span><br><span class="line">  &#125;,</span><br><span class="line">  reducers: &#123;</span><br><span class="line">    increment: state =&gt; &#123;</span><br><span class="line">      // Redux Toolkit allows us to write &quot;mutating&quot; logic in reducers. It</span><br><span class="line">      // doesn&#x27;t actually mutate the state because it uses the immer library,</span><br><span class="line">      // which detects changes to a &quot;draft state&quot; and produces a brand new</span><br><span class="line">      // immutable state based off those changes</span><br><span class="line">      state.value += 1</span><br><span class="line">    &#125;,</span><br><span class="line">    decrement: state =&gt; &#123;</span><br><span class="line">      state.value -= 1</span><br><span class="line">    &#125;,</span><br><span class="line">    incrementByAmount: (state, action) =&gt; &#123;</span><br><span class="line">      // 注意这个action.payload。默认将传入的参数不论是数字还是对象，都将其放在payload中</span><br><span class="line">      state.value += action.payload</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">export const &#123; increment, decrement, incrementByAmount &#125; = counterSlice.actions</span><br><span class="line"></span><br><span class="line">// The function below is called a thunk and allows us to perform async logic. It</span><br><span class="line">// can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This</span><br><span class="line">// will call the thunk with the `dispatch` function as the first argument. Async</span><br><span class="line">// code can then be executed and other actions can be dispatched</span><br><span class="line">export const incrementAsync = amount =&gt; dispatch =&gt; &#123;</span><br><span class="line">  setTimeout(() =&gt; &#123;</span><br><span class="line">    dispatch(incrementByAmount(amount));</span><br><span class="line">  &#125;, 1000);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">// The function below is called a selector and allows us to select a value from</span><br><span class="line">// the state. Selectors can also be defined inline where they&#x27;re used instead of</span><br><span class="line">// in the slice file. For example: `useSelector((state) =&gt; state.counter.value)`</span><br><span class="line">export const selectCount = state =&gt; state.counter.value;</span><br><span class="line"></span><br><span class="line">export default counterSlice.reducer</span><br></pre></td></tr></table></figure>

<h5 id="proxy-immer使用"><a href="#proxy-immer使用" class="headerlink" title="proxy immer使用"></a>proxy immer使用</h5><p><code>createSlice</code> 内部使用了一个名为 <a target="_blank" rel="noopener" href="https://immerjs.github.io/immer/">Immer</a> 的库。 Immer 使用一种称为 “Proxy” 的特殊 JS 工具来包装您提供的数据，当你尝试 ”mutate“ 这些数据的时候，奇迹发生了，<strong>Immer 会跟踪您尝试进行的所有更改，然后使用该更改列表返回一个安全的、不可变的更新值</strong>，就好像您手动编写了所有不可变的更新逻辑一样。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">function handwrittenReducer(state, action) &#123;</span><br><span class="line">  return &#123;</span><br><span class="line">    ...state,</span><br><span class="line">    first: &#123;</span><br><span class="line">      ...state.first,</span><br><span class="line">      second: &#123;</span><br><span class="line">        ...state.first.second,</span><br><span class="line">        [action.someId]: &#123;</span><br><span class="line">          ...state.first.second[action.someId],</span><br><span class="line">          fourth: action.someValue</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 可以变成这样：</span><br><span class="line"></span><br><span class="line">function reducerWithImmer(state, action) &#123;</span><br><span class="line">  state.first.second[action.someId].fourth = action.someValue</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h6 id="警告"><a href="#警告" class="headerlink" title="警告"></a>警告</h6><p><strong>你只能在 Redux Toolkit 的 <code>createSlice</code> 和 <code>createReducer</code> 中编写 “mutation” 逻辑，因为它们在内部使用 Immer！如果你在没有 Immer 的 reducer 中编写 mutation 逻辑，它将改变状态并导致错误！</strong></p>
<h4 id="异步逻辑-thunk"><a href="#异步逻辑-thunk" class="headerlink" title="异步逻辑    thunk"></a>异步逻辑    thunk</h4><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">export const incrementAsync = amount =&gt; dispatch =&gt; &#123;</span><br><span class="line">  setTimeout(() =&gt; &#123;</span><br><span class="line">    dispatch(incrementByAmount(amount))</span><br><span class="line">  &#125;, 1000)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 我们可以像使用普通 Redux action creator 一样使用它们：</span><br><span class="line">store.dispatch(incrementAsync(5))</span><br></pre></td></tr></table></figure>

<p>使用 thunk 需要在创建时将 <code>redux-thunk</code> <em>middleware</em>（一种 Redux 插件）添加到 Redux store 中。幸运的是，Redux Toolkit 的 <code>configureStore</code> 函数已经自动为我们配置好了</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">// 外部的 thunk creator 函数。 进行 AJAX 调用以从服务器获取数据</span><br><span class="line">const fetchUserById = userId =&gt; &#123;</span><br><span class="line">  // 内部的 thunk 函数</span><br><span class="line">  return async (dispatch, getState) =&gt; &#123;</span><br><span class="line">    try &#123;</span><br><span class="line">      // thunk 内发起异步数据请求</span><br><span class="line">      const user = await userAPI.fetchById(userId)</span><br><span class="line">      // 但数据响应完成后 dispatch 一个 action</span><br><span class="line">      dispatch(userLoaded(user))</span><br><span class="line">    &#125; catch (err) &#123;</span><br><span class="line">      // 如果过程出错，在这里处理</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>此外还有个 <strong>createAsyncThunk</strong> 的api，用来<a target="_blank" rel="noopener" href="http://cn.redux.js.org/tutorials/essentials/part-5-async-logic#reducer-%E4%B8%8E-loading-action">创建异步函数</a></p>
<p><a target="_blank" rel="noopener" href="https://codesandbox.io/s/github/reduxjs/redux-essentials-example-app/tree/checkpoint-3-postRequests">示例项目</a></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; createSlice, createAsyncThunk &#125; <span class="keyword">from</span> <span class="string">&#x27;@reduxjs/toolkit&#x27;</span></span><br><span class="line"><span class="keyword">import</span> &#123; client &#125; <span class="keyword">from</span> <span class="string">&#x27;../../api/client&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> initialState = &#123;</span><br><span class="line">  <span class="attr">posts</span>: [],</span><br><span class="line">  <span class="attr">status</span>: <span class="string">&#x27;idle&#x27;</span>,</span><br><span class="line">  <span class="attr">error</span>: <span class="literal">null</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> fetchPosts = createAsyncThunk(<span class="string">&#x27;posts/fetchPosts&#x27;</span>, <span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> response = <span class="keyword">await</span> client.get(<span class="string">&#x27;/fakeApi/posts&#x27;</span>)</span><br><span class="line">  <span class="keyword">return</span> response.data</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> addNewPost = createAsyncThunk(</span><br><span class="line">  <span class="string">&#x27;posts/addNewPost&#x27;</span>,</span><br><span class="line">  <span class="keyword">async</span> (initialPost) =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> response = <span class="keyword">await</span> client.post(<span class="string">&#x27;/fakeApi/posts&#x27;</span>, initialPost)</span><br><span class="line">    <span class="keyword">return</span> response.data</span><br><span class="line">  &#125;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> postsSlice = createSlice(&#123;</span><br><span class="line">  <span class="attr">name</span>: <span class="string">&#x27;posts&#x27;</span>,</span><br><span class="line">  initialState,</span><br><span class="line">  <span class="attr">reducers</span>: &#123;</span><br><span class="line">    <span class="function"><span class="title">reactionAdded</span>(<span class="params">state, action</span>)</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; postId, reaction &#125; = action.payload</span><br><span class="line">      <span class="keyword">const</span> existingPost = state.posts.find(<span class="function">(<span class="params">post</span>) =&gt;</span> post.id === postId)</span><br><span class="line">      <span class="keyword">if</span> (existingPost) &#123;</span><br><span class="line">        existingPost.reactions[reaction]++</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="function"><span class="title">postUpdated</span>(<span class="params">state, action</span>)</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; id, title, content &#125; = action.payload</span><br><span class="line">      <span class="keyword">const</span> existingPost = state.posts.find(<span class="function">(<span class="params">post</span>) =&gt;</span> post.id === id)</span><br><span class="line">      <span class="keyword">if</span> (existingPost) &#123;</span><br><span class="line">        existingPost.title = title</span><br><span class="line">        existingPost.content = content</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="function"><span class="title">extraReducers</span>(<span class="params">builder</span>)</span> &#123;</span><br><span class="line">    builder</span><br><span class="line">      .addCase(fetchPosts.pending, <span class="function">(<span class="params">state, action</span>) =&gt;</span> &#123;</span><br><span class="line">        state.status = <span class="string">&#x27;loading&#x27;</span></span><br><span class="line">      &#125;)</span><br><span class="line">      .addCase(fetchPosts.fulfilled, <span class="function">(<span class="params">state, action</span>) =&gt;</span> &#123;</span><br><span class="line">        state.status = <span class="string">&#x27;succeeded&#x27;</span></span><br><span class="line">        <span class="comment">// Add any fetched posts to the array</span></span><br><span class="line">        state.posts = state.posts.concat(action.payload)</span><br><span class="line">      &#125;)</span><br><span class="line">      .addCase(fetchPosts.rejected, <span class="function">(<span class="params">state, action</span>) =&gt;</span> &#123;</span><br><span class="line">        state.status = <span class="string">&#x27;failed&#x27;</span></span><br><span class="line">        state.error = action.error.message</span><br><span class="line">      &#125;)</span><br><span class="line">      .addCase(addNewPost.fulfilled, <span class="function">(<span class="params">state, action</span>) =&gt;</span> &#123;</span><br><span class="line">        state.posts.push(action.payload)</span><br><span class="line">      &#125;)</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> &#123; postAdded, postUpdated, reactionAdded &#125; = postsSlice.actions</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> postsSlice.reducer</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> selectAllPosts = <span class="function">(<span class="params">state</span>) =&gt;</span> state.posts.posts</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> selectPostById = <span class="function">(<span class="params">state, postId</span>) =&gt;</span></span><br><span class="line">  state.posts.posts.find(<span class="function">(<span class="params">post</span>) =&gt;</span> post.id === postId)</span><br></pre></td></tr></table></figure>





<h4 id="useSelector和useDispatch"><a href="#useSelector和useDispatch" class="headerlink" title="useSelector和useDispatch"></a>useSelector和useDispatch</h4><p>也是一个语法糖，使其更方便的进行使用，而不是像之前那样，使用一个<strong>connect</strong>方法，将state的获取和更新使用props的方式传递进组件</p>
<p>useSelector用来获取store里的state的数据，可直接</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">const posts = useSelector((state) =&gt; state.posts);</span><br></pre></td></tr></table></figure>

<p>还有原生的 <strong>store.getState()</strong> 也可获取state里的数据，但需要直接引入store，有些不好</p>
<p>useDispatch是方便使用dispatch的方法</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">import React, &#123; useState &#125; from &#x27;react&#x27;;</span><br><span class="line">import &#123; useSelector, useDispatch &#125; from &#x27;react-redux&#x27;;</span><br><span class="line">import &#123;</span><br><span class="line">  decrement,</span><br><span class="line">  increment,</span><br><span class="line">  incrementByAmount,</span><br><span class="line">  incrementAsync,</span><br><span class="line">  selectCount,</span><br><span class="line">&#125; from &#x27;./counterSlice&#x27;;</span><br><span class="line">import styles from &#x27;./Counter.module.css&#x27;;</span><br><span class="line"></span><br><span class="line">export function Counter() &#123;</span><br><span class="line">  const count = useSelector(selectCount);</span><br><span class="line">  const dispatch = useDispatch();</span><br><span class="line">  const [incrementAmount, setIncrementAmount] = useState(&#x27;2&#x27;);</span><br><span class="line"></span><br><span class="line">  return (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;div className=&#123;styles.row&#125;&gt;</span><br><span class="line">        &lt;button</span><br><span class="line">          className=&#123;styles.button&#125;</span><br><span class="line">          aria-label=&quot;Increment value&quot;</span><br><span class="line">          onClick=&#123;() =&gt; dispatch(increment())&#125;</span><br><span class="line">        &gt;</span><br><span class="line">          +</span><br><span class="line">        &lt;/button&gt;</span><br><span class="line">        &lt;span className=&#123;styles.value&#125;&gt;&#123;count&#125;&lt;/span&gt;</span><br><span class="line">        &lt;button</span><br><span class="line">          className=&#123;styles.button&#125;</span><br><span class="line">          aria-label=&quot;Decrement value&quot;</span><br><span class="line">          onClick=&#123;() =&gt; dispatch(decrement())&#125;</span><br><span class="line">        &gt;</span><br><span class="line">          -</span><br><span class="line">        &lt;/button&gt;</span><br><span class="line">      &lt;/div&gt;</span><br><span class="line">      &lt;div className=&#123;styles.row&#125;&gt;</span><br><span class="line">        &lt;input</span><br><span class="line">          className=&#123;styles.textbox&#125;</span><br><span class="line">          aria-label=&quot;Set increment amount&quot;</span><br><span class="line">          value=&#123;incrementAmount&#125;</span><br><span class="line">          onChange=&#123;e =&gt; setIncrementAmount(e.target.value)&#125;</span><br><span class="line">        /&gt;</span><br><span class="line">        &lt;button</span><br><span class="line">          className=&#123;styles.button&#125;</span><br><span class="line">          onClick=&#123;() =&gt;</span><br><span class="line">            dispatch(incrementByAmount(Number(incrementAmount) || 0))</span><br><span class="line">          &#125;</span><br><span class="line">        &gt;</span><br><span class="line">          Add Amount</span><br><span class="line">        &lt;/button&gt;</span><br><span class="line">        &lt;button</span><br><span class="line">          className=&#123;styles.asyncButton&#125;</span><br><span class="line">          onClick=&#123;() =&gt; dispatch(incrementAsync(Number(incrementAmount) || 0))&#125;</span><br><span class="line">        &gt;</span><br><span class="line">          Add Async</span><br><span class="line">        &lt;/button&gt;</span><br><span class="line">      &lt;/div&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="prepare函数"><a href="#prepare函数" class="headerlink" title="prepare函数"></a>prepare函数</h4><p>因为在payload里面有可能会使用到一些随机值，并且需要确切的知道payload是什么样子（有点类似ts），所以就需要prepare进行操作</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">const postsSlice = createSlice(&#123;</span><br><span class="line">  name: &#x27;posts&#x27;,</span><br><span class="line">  initialState,</span><br><span class="line">  reducers: &#123;</span><br><span class="line">    postAdded: &#123;</span><br><span class="line">      reducer(state, action) &#123;</span><br><span class="line">        state.push(action.payload)</span><br><span class="line">      &#125;,</span><br><span class="line">      // 这里</span><br><span class="line">      prepare(title, content) &#123;</span><br><span class="line">        return &#123;</span><br><span class="line">          payload: &#123;</span><br><span class="line">            id: nanoid(),	// 生成一个随机id</span><br><span class="line">            title,</span><br><span class="line">            content</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    // other reducers here</span><br><span class="line">    delete()&#123;</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br><span class="line">    </span><br><span class="line">// 调用 dispatch(postAdded(title, content))</span><br></pre></td></tr></table></figure>



<h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h3><h4 id="特别注意"><a href="#特别注意" class="headerlink" title="特别注意"></a>特别注意</h4><ul>
<li><p>在useCallback和useEffect这些里面，第二个参数需要特别的注意，如非必要，不要加上去。同时在第一个参数函数里面，也要注意到底使用了那些变量，修改了那些变量，是否需要加入到第二个参数里面</p>
</li>
<li><p>```html<br>css选择器的一种比较少见的用法</p>
<body>
    <div class="name title mine">
        两个class name连在一起修饰同时具有这两个class的元素
    </div>
    <div class="title name">2222</div>
</body>
<style>
    .name{
        color:darkgoldenrod
    }
    .title{
        background-color: aquamarine;
    }
    .title.mine{
        background-color: blanchedalmond;
    }
</style>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line">  </span><br><span class="line"></span><br><span class="line">- 在typescript中有一个符号 **&amp;** 是一种特殊的用法，表示将两个对象里面的属性进行合并。通常与**Omit**函数配合使用</span><br><span class="line"></span><br><span class="line">  ```ts</span><br><span class="line">  type mine = &#123;</span><br><span class="line">    first: string;</span><br><span class="line">  &#125; &amp; &#123; second: number &#125;;</span><br><span class="line">  </span><br><span class="line">  let my: mine = &#123;first:&#x27;ds&#x27;,second:32&#125;;</span><br></pre></td></tr></table></figure></li>
<li><p><a target="_blank" rel="noopener" href="https://es6.ruanyifeng.com/#docs/module#import-%E5%91%BD%E4%BB%A4">import语句</a>。主要是这种用法</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">&#x27;@/permission&#x27;</span></span><br></pre></td></tr></table></figure>

<p>直接import，而不加 from 。有点类似css的引入。作用似乎是执行所加载的模块</p>
</li>
<li><p>useEffect的第二个参数不要使用直接定义的引用类型</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">import &#123; useEffect, useState, useRef &#125; from &quot;react&quot;;</span><br><span class="line">import useDeepCompareEffect from &quot;./useDeepCompareEffect&quot;;</span><br><span class="line"></span><br><span class="line">export default function DeepCompareEffectComponent() &#123;</span><br><span class="line">  const [age, setAge] = useState(0);</span><br><span class="line">  const [otherCount, setOtherCount] = useState(0);</span><br><span class="line">  const useEffectCountRef = useRef();</span><br><span class="line">  const useDeepCompareEffectCountRef = useRef();</span><br><span class="line"></span><br><span class="line">  let [mine, setMine] = useState(&#123; mine: age &#125;);</span><br><span class="line">  const person = &#123; age: age, name: &quot;Kyle&quot; &#125;;</span><br><span class="line"></span><br><span class="line">  // 注意这个每次otherCount修改之后，下面这个就会被执行一次，似乎是因为从新渲染页面后，person这个在堆的地址就改变了</span><br><span class="line">  // 然后就会执行一次。</span><br><span class="line">  // 解决办法：将这个对象用useState包裹</span><br><span class="line">  useEffect(() =&gt; &#123;</span><br><span class="line">    useEffectCountRef.current.textContent =</span><br><span class="line">      parseInt(useEffectCountRef.current.textContent) + 1;</span><br><span class="line">  &#125;, [person]);</span><br><span class="line"></span><br><span class="line">  useDeepCompareEffect(() =&gt; &#123;</span><br><span class="line">    useDeepCompareEffectCountRef.current.textContent =</span><br><span class="line">      parseInt(useDeepCompareEffectCountRef.current.textContent) + 1;</span><br><span class="line">  &#125;, [person]);</span><br><span class="line"></span><br><span class="line">  useEffect(() =&gt; &#123;</span><br><span class="line">    console.log(&quot;mine: &quot;, mine);</span><br><span class="line">  &#125;, [mine]);</span><br><span class="line"></span><br><span class="line">  return (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;div&gt;</span><br><span class="line">        useEffect: &lt;span ref=&#123;useEffectCountRef&#125;&gt;0&lt;/span&gt;</span><br><span class="line">      &lt;/div&gt;</span><br><span class="line">      &lt;div&gt;</span><br><span class="line">        useDeepCompareEffect: &lt;span ref=&#123;useDeepCompareEffectCountRef&#125;&gt;0&lt;/span&gt;</span><br><span class="line">      &lt;/div&gt;</span><br><span class="line">      &lt;div&gt;Other Count: &#123;otherCount&#125;&lt;/div&gt;</span><br><span class="line">      &lt;div&gt;&#123;JSON.stringify(person)&#125;&lt;/div&gt;</span><br><span class="line">      &lt;div&gt;mine: &#123;JSON.stringify(mine)&#125;&lt;/div&gt;</span><br><span class="line">      &lt;button onClick=&#123;() =&gt; setAge((currentAge) =&gt; currentAge + 1)&#125;&gt;</span><br><span class="line">        Increment Age</span><br><span class="line">      &lt;/button&gt;</span><br><span class="line">      &lt;button onClick=&#123;() =&gt; setOtherCount((count) =&gt; count + 1)&#125;&gt;</span><br><span class="line">        Increment Other Count</span><br><span class="line">      &lt;/button&gt;</span><br><span class="line">      &lt;button onClick=&#123;() =&gt; setMine(&#123; mine: age &#125;)&#125;&gt;mine update age&lt;/button&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
<h4 id="切换主题"><a href="#切换主题" class="headerlink" title="切换主题"></a>切换主题</h4><p>今天看到了一种scss的使用方式，感觉似乎可以用这个来做一个切换主题样式的功能</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">import style from &#x27;./index.scss&#x27;</span><br><span class="line"></span><br><span class="line">const mineStyle = style</span><br><span class="line"></span><br><span class="line">&lt;div className=&#123;mineStyle.title&#125;&gt;</span><br><span class="line">...</span><br><span class="line">&lt;/div&gt;</span><br></pre></td></tr></table></figure>

<p>就不适用普通的直接引入，而是命名引入，然后可以引入多个scss文件，最后逻辑判断使用那一个</p>
<h4 id="特殊小技巧"><a href="#特殊小技巧" class="headerlink" title="特殊小技巧"></a>特殊小技巧</h4><p>1.可通过自定义指令来判断元素是否可被当前用户所看见，</p>
<p>就将之前用一大堆的v-if判断，v-else要好一些了</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; useStore &#125; <span class="keyword">from</span> <span class="string">&#x27;@/store&#x27;</span></span><br><span class="line"><span class="keyword">import</span> &#123; Directive &#125; <span class="keyword">from</span> <span class="string">&#x27;vue&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> permission: Directive = &#123;</span><br><span class="line">  <span class="function"><span class="title">mounted</span>(<span class="params">el, binding</span>)</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; value &#125; = binding</span><br><span class="line">    <span class="keyword">const</span> roles = useStore().state.user.roles</span><br><span class="line">    <span class="keyword">if</span> (value &amp;&amp; value <span class="keyword">instanceof</span> <span class="built_in">Array</span> &amp;&amp; value.length &gt; <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="keyword">const</span> permissionRoles = value</span><br><span class="line">      <span class="keyword">const</span> hasPermission = roles.some(<span class="function">(<span class="params">role: any</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> permissionRoles.includes(role)</span><br><span class="line">      &#125;)</span><br><span class="line">      <span class="keyword">if</span> (!hasPermission) &#123;</span><br><span class="line">        el.style.display = <span class="string">&#x27;none&#x27;</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">&#x27;need roles! Like v-permission=&quot;[\&#x27;admin\&#x27;,\&#x27;editor\&#x27;]&quot;&#x27;</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>2.使用原生js的contains方法实现判断外部点击</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">import &#123; useRef, useState &#125; from &quot;react&quot;</span><br><span class="line">import useClickOutside from &quot;./useClickOutside&quot;</span><br><span class="line"></span><br><span class="line">export default function ClickOutsideComponent() &#123;</span><br><span class="line">  const [open, setOpen] = useState(false)</span><br><span class="line">  const modalRef = useRef()</span><br><span class="line"></span><br><span class="line">  useClickOutside(modalRef, () =&gt; &#123;</span><br><span class="line">    if (open) setOpen(false)</span><br><span class="line">  &#125;)</span><br><span class="line"></span><br><span class="line">  return (</span><br><span class="line">    &lt;&gt;</span><br><span class="line">      &lt;button onClick=&#123;() =&gt; setOpen(true)&#125;&gt;Open&lt;/button&gt;</span><br><span class="line">      &lt;div</span><br><span class="line">        ref=&#123;modalRef&#125;</span><br><span class="line">        style=&#123;&#123;</span><br><span class="line">          display: open ? &quot;block&quot; : &quot;none&quot;,</span><br><span class="line">          backgroundColor: &quot;blue&quot;,</span><br><span class="line">          color: &quot;white&quot;,</span><br><span class="line">          width: &quot;100px&quot;,</span><br><span class="line">          height: &quot;100px&quot;,</span><br><span class="line">          position: &quot;absolute&quot;,</span><br><span class="line">          top: &quot;calc(50% - 50px)&quot;,</span><br><span class="line">          left: &quot;calc(50% - 50px)&quot;,</span><br><span class="line">        &#125;&#125;</span><br><span class="line">      &gt;</span><br><span class="line">        &lt;span&gt;Modal&lt;/span&gt;</span><br><span class="line">      &lt;/div&gt;</span><br><span class="line">    &lt;/&gt;</span><br><span class="line">  )</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">import useEventListener from &quot;../13-useEventListener/useEventListener&quot;</span><br><span class="line"></span><br><span class="line">export default function useClickOutside(ref, cb) &#123;</span><br><span class="line">  useEventListener(</span><br><span class="line">    &quot;click&quot;,</span><br><span class="line">    e =&gt; &#123;</span><br><span class="line">      // 注意下面的contains方法</span><br><span class="line">      if (ref.current == null || ref.current.contains(e.target)) return</span><br><span class="line">      cb(e)</span><br><span class="line">    &#125;,</span><br><span class="line">    document</span><br><span class="line">  )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>3.<a target="_blank" rel="noopener" href="https://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html">使用IntersectionObserver api判断一个元素是否进行视口</a></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> io = <span class="keyword">new</span> IntersectionObserver(callback, option);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 开始观察</span></span><br><span class="line">io.observe(<span class="built_in">document</span>.getElementById(<span class="string">&#x27;example&#x27;</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 停止观察</span></span><br><span class="line">io.unobserve(element);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 关闭观察器</span></span><br><span class="line">io.disconnect();</span><br></pre></td></tr></table></figure>



<p><a target="_blank" rel="noopener" href="https://qastack.cn/programming/44446671/my-position-sticky-element-isnt-sticky-when-using-flexbox">4.flex布局中使用sticky定位</a></p>
<p>需要在sticky定位的元素上添加</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">align-self: flex-start;</span><br></pre></td></tr></table></figure>

<p>不然sticky定位不生效，因为flex自动将内部的元素的高度设为一样</p>
<p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/2imliGUT9XrIrtHCCyyYgw">5.css画三角</a></p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">.triangle &#123;</span><br><span class="line">    width: 0;</span><br><span class="line">    height: 0;</span><br><span class="line">    /* border-top: 100px solids skyblue; */</span><br><span class="line">    border-bottom: 100px solid #000000;</span><br><span class="line">    /* border-right: 100px solid seagreen; */</span><br><span class="line">    border-left: 100px solid darkgoldenrod;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注意left、right和top、bottom是配合的，当只出现其中三个且宽度相同的时候，最大的三角形就是没有配对的那一个，如left,right,top，出现的最大的就是top的那一个三角形</p>
<p>当只出现两个的时候，必须是不配对的，如left和top。若出现left和right，则不会有效果。同时出现的是完整的正方形所对应边的四分之一</p>
</div></article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="mailto:undefined">晟松</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://chengsong.info/2021/11/19/react学习记录/">https://chengsong.info/2021/11/19/react学习记录/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://chengsong.info">晟松</a>！</span></div></div><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/react-redux-hook/">react redux hook</a></div><nav id="pagination"><div class="prev-post pull-left"><a href="/2021/11/19/umijs+dva%E7%AE%80%E5%8D%95%E5%AD%A6%E4%B9%A0/"><i class="fa fa-chevron-left">  </i><span>umijs+dva</span></a></div><div class="next-post pull-right"><a href="/2021/11/19/mini-vue3%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/"><span>mini-vue3</span><i class="fa fa-chevron-right"></i></a></div></nav></div></div><footer class="footer-bg" style="background-image: url(/img/blog-bg.jpg)"><div class="layout" id="footer"><div class="copyright">&copy;2020 - 2023 By 晟松</div><div class="framework-info"><span>驱动 - </span><a target="_blank" rel="noopener" href="http://hexo.io"><span>Hexo</span></a><span class="footer-separator">|</span><span>主题 - </span><a target="_blank" rel="noopener" href="https://github.com/Molunerfinn/hexo-theme-melody"><span>Melody</span></a></div><div class="icp"><a target="_blank" rel="noopener" href="http://www.beian.miit.gov.cn/"><span>湘ICP备2020021729号</span></a></div><div class="busuanzi"><script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><span id="busuanzi_container_page_pv"><i class="fa fa-file"></i><span id="busuanzi_value_page_pv"></span><span></span></span></div></div></footer><i class="fa fa-arrow-up" id="go-up" aria-hidden="true"></i><script src="https://cdn.jsdelivr.net/npm/animejs@latest/anime.min.js"></script><script src="https://cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.js"></script><script src="https://cdn.jsdelivr.net/npm/velocity-animate@latest/velocity.min.js"></script><script src="https://cdn.jsdelivr.net/npm/velocity-ui-pack@latest/velocity.ui.min.js"></script><script src="/js/utils.js?version=1.9.0"></script><script src="/js/fancybox.js?version=1.9.0"></script><script src="/js/sidebar.js?version=1.9.0"></script><script src="/js/copy.js?version=1.9.0"></script><script src="/js/fireworks.js?version=1.9.0"></script><script src="/js/transition.js?version=1.9.0"></script><script src="/js/scroll.js?version=1.9.0"></script><script src="/js/head.js?version=1.9.0"></script><script src="/js/search/local-search.js"></script><script>if(/Android|webOS|iPhone|iPod|iPad|BlackBerry/i.test(navigator.userAgent)) {
  $('#nav').addClass('is-mobile')
  $('footer').addClass('is-mobile')
  $('#top-container').addClass('is-mobile')
}</script><div class="search-dialog" id="local-search"><div class="search-dialog__title" id="local-search-title">本地搜索</div><div id="local-input-panel"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章"></div></div></div><hr><div id="local-search-results"><div id="local-hits"></div><div id="local-stats"><div class="local-search-stats__hr" id="hr"><span>由</span> <a target="_blank" rel="noopener" href="https://github.com/wzpan/hexo-generator-search" style="color:#49B1F5;">hexo-generator-search</a>
 <span>提供支持</span></div></div></div><span class="search-close-button"><i class="fa fa-times"></i></span></div><div class="search-mask"></div></body></html>